//
//  CodableExample.swift
//  SwiftCode
//
//  Created by 于虎拴 on 2023/1/11.
//

import Foundation
import Utility

final class CodableExample: ListViewController {
  override func getItemCount() -> Int {
    data.count
  }
  
  override func getItem(_ indexPath: IndexPath) -> Item {
    data[indexPath.row]
  }
  
  private lazy var data: [Item] = [
    .init(title: "解析为模型", description: "基本模型解码", action: testDecodeModel),
    .init(title: "解析为模型数组", description: "基本模型数组解码", action: testDecodeModelArray),
    .init(title: "解析为字典", description: "字典类型解码", action: testDecodeDictionary),
    .init(title: "不确定类型解析", description: "根据type确定类型", action: testDecodeByType),
    .init(title: "解析测试", description: "", action: testDecode),
  ]
}

private func testDecodeModel() {
  do {
    try decodeModel()
  } catch {
    print(error)
  }
}

private func testDecodeModelArray() {
  do {
    try decodeModelArray()
  } catch {
    print(error)
  }
}

private func testDecodeDictionary() {
  do {
    try decodeDictionary()
  } catch {
    print(error)
  }
}

private func testDecodeByType() {
  do {
    try decodeByType()
  } catch {
    print(error)
  }
}

private struct UserInfo: Codable {
  let name: String
  let age: Int
  let birthday: Date
  let gender: Gender
}

private enum Gender: Int, Codable {
  // 保密
  case unknown = 0
  // 男
  case male
  // 女
  case female
  
  init?(rawValue: Int) {
    switch rawValue {
    case 1: self = .male
    case 2: self = .female
    default: self = .unknown
    }
  }
}

/**
 解析基本数据类型
 
 报错 keyNotFound，Json字符串缺少Key
 - self.age = (try? container.decode(Int.self, forKey: .age)) ??
 - var age: Int?
 
 case "CodingKey和Json类型不匹配"
 - self.age = (try? container.decode(Int.self, forKey: .age)) ?? 0
 */
private func decodeModel() throws {
  print("====================================", #function, "====================================")
  let decodeJson = """
  {
    "name": "zhangsan",
    "age": 25,
    "birthday": "2022-09-12T10:25:41+00:00",
    "gender": 1
  }
  """
  
  print("解码——模型:")
  let decodeData = decodeJson.data(using: .utf8)!
  let decoder = JSONDecoder()
  decoder.dateDecodingStrategy = .iso8601
  let decodeModel = try decoder.decode(UserInfo.self, from: decodeData)
  print(decodeModel)
  
  print("编码——字符串:")
//  let encodeData = try JSONEncoder().encode(decodeModel)
  let product = Product()
  let encodeData = try JSONEncoder().encode(product)
  let encodeJson = String(data: encodeData, encoding: .utf8)!
  print(encodeJson)
}

private struct Product: Codable {
  var price: Decimal = NSDecimalNumber(string: "4.99").decimalValue
}

private struct RolePortrait: Codable {
  let id: String
  let portrait: String
  let wholeBodyImage: String
}

private func decodeModelArray() throws {
  print("====================================", #function, "====================================")
  let decodeJson = """
  [{"id":"1","portrait":"https://na-cdn.playza.fun/frontend/upload/Portrait1.png","wholeBodyImage":"https://na-cdn.playza.fun/frontend/upload/1CharacterFull1.png"},
   {"id":"2","portrait":"https://na-cdn.playza.fun/frontend/upload/Portrait2.png","wholeBodyImage":"https://na-cdn.playza.fun/frontend/upload/2CharacterFull2.png"},
   {"id":"3","portrait":"https://na-cdn.playza.fun/frontend/upload/Portrait3.png","wholeBodyImage":"https://na-cdn.playza.fun/frontend/upload/3CharacterFull3.png"},
   {"id":"4","portrait":"https://na-cdn.playza.fun/frontend/upload/Portrait4.png","wholeBodyImage":"https://na-cdn.playza.fun/frontend/upload/4CharacterFull4.png"},
   {"id":"5","portrait":"https://na-cdn.playza.fun/frontend/upload/Portrait5.png","wholeBodyImage":"https://na-cdn.playza.fun/frontend/upload/5CharacterFull5.png"},
   {"id":"6","portrait":"https://na-cdn.playza.fun/frontend/upload/Portrait6.png","wholeBodyImage":"https://na-cdn.playza.fun/frontend/upload/6CharacterFull6.png"},
   {"id":"7","portrait":"https://na-cdn.playza.fun/frontend/upload/Portrait7.png","wholeBodyImage":"https://na-cdn.playza.fun/frontend/upload/7CharacterFull7.png"}]
  """
  
  print("解码——模型:")
  let decodeData = decodeJson.data(using: .utf8)!
  let decoder = JSONDecoder()
  
  let decodeModel = try decoder.decode([RolePortrait].self, from: decodeData)
  print(decodeModel)
  
  print("编码——字符串:")
  let encodeData = try JSONEncoder().encode(decodeModel)
  let encodeJson = String(data: encodeData, encoding: .utf8)!
  print(encodeJson)
}

private func decodeDictionary() throws {
  print("====================================", #function, "====================================")
  let decodeJson = """
  {
    "name": "zhangsan",
    "age": 25,
    "man": true,
    "father": {
      "name": "zhangsanshi",
      "age": 52,
      "man": true
    }
  }
  """
  print("json:")
  print(decodeJson)
  
  print("解码——字典:")
  let decodeData = decodeJson.data(using: .utf8)!
  let decodeModel = try JSONSerialization.jsonObject(with: decodeData, options: []) as? [String: Any]
  print(decodeModel as Any)
  
  print("编码——字符串:")
  let encodeData = try JSONSerialization.data(withJSONObject: decodeModel!, options: [])
  let encodeJson = String(data: encodeData, encoding: .utf8)!
  print(encodeJson)
}

private struct MessageInfo: Codable {
  let from: String
  let text: String
  let attachment: [Attachment]
}

private enum Attachment {
  case image(ImageAttachment)
  case audio(AudioAttachment)
  case unsupported
}

private struct ImageAttachment: Codable {
  let url: URL
  let width: Int
  let height: Int
}

private struct AudioAttachment: Codable {
  let title: String
  let url: URL
  let shouldAutoplay: Bool
}

extension Attachment: Codable {
  private enum CodingKeys: CodingKey {
    case type
    case payload
  }
  
  init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    let type = try container.decode(String.self, forKey: .type)
    
    // The attachment data is nested if it has the `payload` key
    let isNested = container.allKeys.contains(.payload)
    switch type {
    case "image":
      let payload = try isNested ? container.decode(ImageAttachment.self, forKey: .payload) : ImageAttachment(from: decoder)
      self = .image(payload)
    case "audio":
      let payload = try isNested ? container.decode(AudioAttachment.self, forKey: .payload) : AudioAttachment(from: decoder)
      self = .audio(payload)
    default:
      self = .unsupported
    }
  }
  
  func encode(to encoder: Encoder) throws {
    var container = encoder.container(keyedBy: CodingKeys.self)
    switch self {
    case .image(let imageAttachment):
      try container.encode("image", forKey: .type)
      try container.encode(imageAttachment, forKey: .payload)
    case .audio(let audioAttachment):
      try container.encode("audio", forKey: .type)
      try container.encode(audioAttachment, forKey: .payload)
    case .unsupported:
      try container.encode("unsupported", forKey: .type)
    }
  }
}

/// 参考：
/// - 文字：https://ios.dovov.com/swift中带codable的不确定类型.html
/// - 代码：http://ask.sov5.cn/q/MOwwDQB5gm
private func decodeByType() throws {
  print("====================================", #function, "====================================")
  let decodeJson = #"""
    {
      "from": "Guille",
      "text": "Look what I just found!",
      "attachment": [{
        "type": "image",
        "payload": {
          "url": "http://via.placeholder.com/640x480",
          "width": 640,
          "height": 480
        }
      }, {
        "type": "audio",
        "title": "Never Gonna Give You Up",
        "url": "https://audio.com/NeverGonnaGiveYouUp.mp3",
        "shouldAutoplay": true
      }]
    }
    """#
  print("json:")
  print(decodeJson)
  
  print("解码——模型:")
  let decodeData = decodeJson.data(using: .utf8)!
  let decodeModel = try JSONDecoder().decode(MessageInfo.self, from: decodeData)
  print(decodeModel)
  
  print("编码——字符串:")
  let encodeModel = try JSONEncoder().encode(decodeModel)
  let encodeJson = String(data: encodeModel, encoding: .utf8)!
  print(encodeJson)
}


private func testDecode() {
  do {
    try decodeTest()
  } catch {
    print(error)
  }
}

public struct OrderParam: Codable {
  /// 支付ark总数(原价,单位:个)
  public let amount: Int
  /// 游戏 ID
  public let gameId: String
  /// 商品数量
  public let count: Int
  /// 拓展透传字段
  public let cpExtra: String?
  /// 随机串
  public let nonce: String
  /// cp订单号(mw订单号)
  public let cpOrderId: String
  /// 实际支付ark总数(单位:个)
  public let payAmount: Int
  /// 商品code
  public let productCode: String
  /// 商品名
  public let productName: String
  /// 商品价格
  public let productPrice: Int
  /// 业务场景编号。102为联运Ark币消耗，海外是PG币消耗
  public let sceneCode: Int
  /// 支付方式。64: ARK币支付, 65: 谷歌支付, 66: 苹果支付, 67: PG币支付
  public let payChannel: Int?
  /// 支付渠道 1: APP 即可
  public let payTunnel: Int
  public let productList: [MWProduct]?
  
  public struct MWProduct: Codable {
    let commodityId: String
    let commodityName: String
  }
}

private func decodeTest() throws {
  print("====================================", #function, "====================================")
  let decodeJson = #"""
    {
    "gameId": "567351",
    "nonce": "355256",
    "cpOrderId": "1ef65e0a26d86aae82c700163e0e6450",
    "amount": 1530,
    "payAmount": 1530,
    "sceneCode": 113,
    "payChannel": 64,
    "payTunnel": 1,
    "count": 0,
    "productCode": "",
    "productPrice": 0,
    "productList": [
      {
        "commodityId": "6VNc3YoZXlj000a0k",
        "commodityName": "开学季服装8女上衣",
        "commodityOriginalPrice": 362,
        "commodityPrice": 362,
        "extra": "{\"discount\":100}",
        "gameId": "567351",
        "number": 1
      },
      {
        "commodityId": "1DFtPOxAGx3000a0l",
        "commodityName": "开学季服装8女下衣",
        "commodityOriginalPrice": 325,
        "commodityPrice": 325,
        "extra": "{\"discount\":100}",
        "gameId": "567351",
        "number": 1
      },
      {
        "commodityId": "9BuHjAwgw8M000a0m",
        "commodityName": "开学季服装8女鞋",
        "commodityOriginalPrice": 363,
        "commodityPrice": 363,
        "extra": "{\"discount\":100}",
        "gameId": "567351",
        "number": 1
      },
      {
        "commodityId": "8JJ9Z97GwN0000a0j",
        "commodityName": "开学季服装8女整体发型",
        "commodityOriginalPrice": 400,
        "commodityPrice": 400,
        "extra": "{\"discount\":100}",
        "gameId": "567351",
        "number": 1
      },
      {
        "commodityId": "8yyBhJdsTVv000a0n",
        "commodityName": "开学季服装8女配饰",
        "commodityOriginalPrice": 80,
        "commodityPrice": 80,
        "extra": "{\"discount\":100}",
        "gameId": "567351",
        "number": 1
      }
    ],
    "productName": "角色展示游戏",
    "payType": 1
    }
    """#
//  print("json:")
//  print(decodeJson)
  
  print("解码——字典:")
  let decodeData = decodeJson.data(using: .utf8)!
  let decodeModel = try JSONSerialization.jsonObject(with: decodeData, options: []) as? [String: Any]
  print(decodeModel as Any)
  
  guard let data = decodeModel,
        let order = OrderParam(json: data)
  else { return }
  
  print("解码——order:")
  print(order)
  print(order.productList?.map(\.commodityId).joined(separator: ","))
  
  print("编码——字符串:")
  let callback: [String: Any] = ["code": 200,
   "message": "OK",
   "amount": 34,
   "source": "source",
   "productList": data["productList"] as Any
  ]
  
  let encodeData = try JSONSerialization.data(withJSONObject: callback, options: [])
  let encodeJson = String(data: encodeData, encoding: .utf8)!
  print(encodeJson)
}

public struct TransactionInfo: SinkEntity {
  /// 交易订单号 用于轮询是否发货
  public let purchaseType: Int?
}
